Connectivity Software User's Guide and Reference
Hosting OPC Wizard Servers
OPC Wizard > Concepts > Hosting OPC Wizard Servers
In This Topic

The code that implements the OPC server (using OPC Wizard) cannot just be written and built - it ultimately needs to be run, and for it it needs to be hosted in some kind of executable application. There are many hosting options; the OPC Wizard does no artificially limit them, but some are more common and practical than others. This article will specifically address certain typical hosting scenarios. However, other hosting options exist as well.

In all cases, your code needs to create an instance of the EasyUAServer Class and define its properties and populate the node space. It then needs to call the Start Method on the server object. When this happens depends on the hosting scenario. The OPC server is started, and it keeps running. At some later moment, when the server needs to be shut down, your code should call the Stop Method. The host may require additional code in order to integrate it with your server.

Console Host

Hosting the OPC Server in a console application is probably the easiest option in terms of coding effort needed to implement it; and the same time, it is also very easy to debug. There is almost no extra code necessary to make the server work inside the console application. 

If you enable Unsolicited User Interaction in the console (as in the example below), make sure that your application does not prompt for any user input of its own at the console while the server is running. The unsolicited input requests from the OPC Wizard may collide with those from your application.

The following example illustrates a fully functional OPC Wizard-based server, hosted in a console application. The server also outputs information related to the state of its endpoints, and client connections and disconnections.

// A fully functional OPC UA demo server running in a console host.
//
// Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-ConnectivityStudio/Latest/examples.html .
// OPC client, server and subscriber examples in C# on GitHub: https://github.com/OPCLabs/Examples-ConnectivityStudio-CSharp .
// Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
// a commercial license in order to use Online Forums, and we reply to every post.

using System;
using System.Threading;
using Microsoft.Extensions.DependencyInjection;
using OpcLabs.EasyOpc.UA;
using OpcLabs.EasyOpc.UA.Services;
using UAServerDemoLibrary;

namespace UAServerConsoleDemo
{
    internal class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("OPC Wizard Server Console Demo");
            Console.WriteLine();

            // Enable the console interaction by the server.
            EasyUAServer.SharedParameters.PluginSetups.FindName("UAConsoleInteraction").Enabled = true;

            // Instantiate the server object.
            // By default, the server will run on endpoint URL "opc.tcp://localhost:48040/".
            using (var server = new EasyUAServer())
            {
                // Define various nodes.
                ConsoleNodes.AddToParent(server.Objects);
                DataNodes.AddToParent(server.Objects);
                DemoNodes.AddToParent(server.Objects);

                // Hook events to the server object.
                server.EndpointStateChanged += (sender, eventArgs) =>
                    Console.WriteLine($"{nameof(server.EndpointStateChanged)}: {eventArgs}");
                server.Starting += (sender, eventArgs) => Console.WriteLine(nameof(server.Starting));
                server.Stopped += (sender, eventArgs) => Console.WriteLine(nameof(server.Stopped));

                // Obtain the server connection monitoring service.
                IEasyUAServerConnectionMonitoring serverConnectionMonitoring = server.GetService<IEasyUAServerConnectionMonitoring>();
                if (!(serverConnectionMonitoring is null))
                {
                    // Hook events to the connection monitoring service.
                    serverConnectionMonitoring.ClientSessionConnected += (sender, eventArgs) =>
                        Console.WriteLine($"{nameof(serverConnectionMonitoring.ClientSessionConnected)}: {eventArgs}");
                    serverConnectionMonitoring.ClientSessionDisconnected += (sender, eventArgs) =>
                        Console.WriteLine($"{nameof(serverConnectionMonitoring.ClientSessionDisconnected)}: {eventArgs}");
                }

                // Start the server.
                server.Start();

                // Let the user decide when to stop.
                var cancelled = new ManualResetEvent(initialState: false);
                Console.CancelKeyPress += (sender, eventArgs) =>
                {
                    // Signal the main thread to exit.
                    cancelled.Set();

                    // Prevent the process from terminating immediately.
                    eventArgs.Cancel = true;
                };

                Console.WriteLine("Press Ctrl+C to stop the server...");
                cancelled.WaitOne();

                // Stop the server.
                server.Stop();
            }
        }
    }
}
' A fully functional OPC UA demo server running in a console host.
'
' Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-ConnectivityStudio/Latest/examples.html .
' OPC client and subscriber examples in VB.NET on GitHub: https://github.com/OPCLabs/Examples-ConnectivityStudio-VBNET .
' Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
' a commercial license in order to use Online Forums, and we reply to every post.

Imports System
Imports System.Threading
Imports Microsoft.Extensions.DependencyInjection
Imports OpcLabs.BaseLib
Imports OpcLabs.EasyOpc.UA
Imports OpcLabs.EasyOpc.UA.Services
Imports UAServerDemoLibrary

Namespace Global.UAServerConsoleDemo
    Module Program
        Sub Main(args As String())
            Console.WriteLine("OPC Wizard Server Console Demo")
            Console.WriteLine("")

            ' Enable the console interaction by the server.
            EasyUAServer.SharedParameters.PluginSetups.FindName("UAConsoleInteraction").Enabled = True

            ' Instantiate the server object.
            ' By default, the server will run on endpoint URL "opc.tcp://localhost:48040/".
            Using server = New EasyUAServer()
                ' Define various nodes.
                ConsoleNodes.AddToParent(server.Objects)
                DataNodes.AddToParent(server.Objects)
                DemoNodes.AddToParent(server.Objects)

                ' Hook events to the server object.
                AddHandler server.EndpointStateChanged, Sub(sender, EventArgs) _
                                                           Console.WriteLine($"{NameOf(server.EndpointStateChanged)}: {EventArgs}")
                AddHandler server.Starting, Sub(sender, EventArgs) Console.WriteLine(NameOf(server.Starting))
                AddHandler server.Stopped, Sub(sender, EventArgs) Console.WriteLine(NameOf(server.Stopped))

                ' Obtain the server connection monitoring service.
                Dim serverConnectionMonitoring As IEasyUAServerConnectionMonitoring = server.GetService(Of IEasyUAServerConnectionMonitoring)()
                If Not (serverConnectionMonitoring Is Nothing) Then
                    ' Hook events to the connection monitoring service.
                    AddHandler serverConnectionMonitoring.ClientSessionConnected, Sub(sender, EventArgs) _
                        Console.WriteLine($"{NameOf(serverConnectionMonitoring.ClientSessionConnected)}: {EventArgs}")
                    AddHandler serverConnectionMonitoring.ClientSessionDisconnected, Sub(sender, EventArgs) _
                        Console.WriteLine($"{NameOf(serverConnectionMonitoring.ClientSessionDisconnected)}: {EventArgs}")
                End If

                ' Start the server.
                server.Start()

                ' Let the user decide when to stop.
                Dim cancelled = New ManualResetEvent(initialState:=False)
                AddHandler Console.CancelKeyPress, Sub(sender, EventArgs) _
                                                       ' Signal the main thread to exit.
                                                       cancelled.Set()

                                                       ' Prevent the process from terminating immediately.
                                                       EventArgs.Cancel = True
                                                   End Sub

                Console.WriteLine("Press Ctrl+C to stop the server...")
                cancelled.WaitOne()

                ' Stop the server.
                server.Stop()
            End Using
        End Sub
    End Module
End Namespace

Windows Forms Host

OPC servers developed with OPC Wizard can be hosted inside a Windows desktop application developed using Windows Forms (and in fact, the application can also be developed using WPF or other tools). This hosting option provides a possibility of integrating the OPC server with rich user interface, inside the same process.

One advantage of hosting the OPC server in Windows Forms application is that it can be easily integrated with the Administer OPC UA Application Dialog.

Example: Installed Examples - Server Windows Forms - UAServerWindowsFormsDemo

Windows Service Host

Hosting your OPC server in a Windows service is meant mainly for "head-less", fully automated operations. The service has several advantages over other hosting options, The service can be started and stopped in various ways provided by the operating system, and it can even start together with the operating system. The service can operate without any interactive user being logged in, and it can be configured to run under a specific user account. See e.g. the ServiceBase Class documentation for more information on developing Windows Services in this way.

Windows Services developed in this way, i.e. with the use of ServiceBase Class, represent the "old" way of developing service-like applications in .NET. This approach is available in .NET Framework. In new projects and in .NET 8+, it is recommended to use the Worker Service approach, described further below.

Example: Installed Examples - Server Windows Service - UAServerWindowsServiceDemo

Worker Service Host

The "worker service" (Worker services in .NET) is a basically a modern way of creating a service-like application in .NET - not just Windows service, but eventually also a Linux daemon, etc.

The following example illustrates how an OPC server hosted in a worker service can be created.

// UAServerWorkerServiceDemo: Shows how to use the component to create an OPC UA server hosted in a worker service. It
// provides readable and writable nodes of various types.
// See also: https://learn.microsoft.com/en-us/dotnet/core/extensions/windows-service
//
// Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-ConnectivityStudio/Latest/examples.html .
// OPC client, server and subscriber examples in C# on GitHub: https://github.com/OPCLabs/Examples-ConnectivityStudio-CSharp .
// Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
// a commercial license in order to use Online Forums, and we reply to every post.

using Microsoft.Extensions.Logging.Configuration;
using Microsoft.Extensions.Logging.EventLog;

namespace UAServerWorkerServiceDemo
{
    public class Program
    {
        public static void Main(string[] args)
        {
            HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
            builder.Services.AddWindowsService(options =>
            {
                options.ServiceName = "OpcWizardDemo";
            });

            LoggerProviderOptions.RegisterProviderOptions<
                EventLogSettings, EventLogLoggerProvider>(builder.Services);

            builder.Services.AddHostedService<ServerDemoBackgroundService>();

            IHost host = builder.Build();
            host.Run();
        }
    }
}
' UAServerWorkerServiceDemo: Shows how to use the component to create an OPC UA server hosted in a worker service. It
' provides readable and writable nodes of various types.
' See also: https://learn.microsoft.com/en-us/dotnet/core/extensions/windows-service
'
' Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-ConnectivityStudio/Latest/examples.html .
' OPC client and subscriber examples in VB.NET on GitHub: https://github.com/OPCLabs/Examples-ConnectivityStudio-VBNET .
' Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
' a commercial license in order to use Online Forums, and we reply to every post.

Imports Microsoft.Extensions.Logging.Configuration
Imports Microsoft.Extensions.Logging.EventLog

Module Program
    Sub Main(args As String())
        Dim builder As HostApplicationBuilder = Host.CreateApplicationBuilder(args)
        builder.Services.AddWindowsService(Sub(options)
                                               options.ServiceName = "OpcWizardDemo"
                                           End Sub
        )

        LoggerProviderOptions.RegisterProviderOptions(Of EventLogSettings, EventLogLoggerProvider) _
                (builder.Services)

        builder.Services.AddHostedService(Of ServerDemoBackgroundService)()

        Dim host0 As IHost = builder.Build()
        host0.Run()
    End Sub
End Module
// UAServerWorkerServiceDemo: Shows how to use the component to create an OPC UA server hosted in a worker service. It
// provides readable and writable nodes of various types.
// See also: https://learn.microsoft.com/en-us/dotnet/core/extensions/windows-service
//
// Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-ConnectivityStudio/Latest/examples.html .
// OPC client, server and subscriber examples in C# on GitHub: https://github.com/OPCLabs/Examples-ConnectivityStudio-CSharp .
// Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
// a commercial license in order to use Online Forums, and we reply to every post.

using OpcLabs.EasyOpc.UA;
using UAServerDemoLibrary;

namespace UAServerWorkerServiceDemo
{
    public class ServerDemoBackgroundService : BackgroundService
    {
        private readonly ILogger<ServerDemoBackgroundService> _logger;

        // The server object.
        // By default, the server will run on endpoint URL "opc.tcp://localhost:48040/".
        private readonly EasyUAServer _server = new();

        public ServerDemoBackgroundService(ILogger<ServerDemoBackgroundService> logger)
        {
            _logger = logger;

            // Define various nodes.
            ConsoleNodes.AddToParent(_server.Objects);
            DataNodes.AddToParent(_server.Objects);
            DemoNodes.AddToParent(_server.Objects);
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            try
            {
                // Start the server.
                _server.Start();

                while (!stoppingToken.IsCancellationRequested)
                {
                    await Task.Delay(1000, stoppingToken);
                }

                // Stop the server.
                _server.Stop();
            }
            catch (OperationCanceledException)
            {
                // When the stopping token is canceled, for example, a call made from services.msc,
                // we shouldn't exit with a non-zero exit code. In other words, this is expected...
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "{Message}", ex.Message);

                // Terminates this process and returns an exit code to the operating system.
                // This is required to avoid the 'BackgroundServiceExceptionBehavior', which
                // performs one of two scenarios:
                // 1. When set to "Ignore": will do nothing at all, errors cause zombie services.
                // 2. When set to "StopHost": will cleanly stop the host, and log errors.
                //
                // In order for the Windows Service Management system to leverage configured
                // recovery options, we need to terminate the process with a non-zero exit code.
                Environment.Exit(1);
            }
        }
    }
}
' UAServerWorkerServiceDemo: Shows how to use the component to create an OPC UA server hosted in a worker service. It
' provides readable and writable nodes of various types.
' See also: https://learn.microsoft.com/en-us/dotnet/core/extensions/windows-service
'
' Find all latest examples here: https://opclabs.doc-that.com/files/onlinedocs/OPCLabs-ConnectivityStudio/Latest/examples.html .
' OPC client and subscriber examples in VB.NET on GitHub: https://github.com/OPCLabs/Examples-ConnectivityStudio-VBNET .
' Missing some example? Ask us for it on our Online Forums, https://www.opclabs.com/forum/index ! You do not have to own
' a commercial license in order to use Online Forums, and we reply to every post.

Imports OpcLabs.EasyOpc.UA
Imports UAServerDemoLibrary
Public Class ServerDemoBackgroundService
    Inherits BackgroundService

    Private ReadOnly _logger As ILogger(Of ServerDemoBackgroundService)

    ' The server object.
    ' By default, the server will run on endpoint URL "opc.tcp://localhost:48040/".
    Private ReadOnly _server As EasyUAServer = New EasyUAServer()

    Public Sub New(logger As ILogger(Of ServerDemoBackgroundService))
        _logger = logger

        ' Define various nodes.
        ConsoleNodes.AddToParent(_server.Objects)
        DataNodes.AddToParent(_server.Objects)
        DemoNodes.AddToParent(_server.Objects)
    End Sub

    Protected Overrides Async Function ExecuteAsync(stoppingToken As Threading.CancellationToken) As Task
        Try
            ' Start the server.
            _server.Start()

            While (Not stoppingToken.IsCancellationRequested)
                Await Task.Delay(1000, stoppingToken)
            End While

            ' Stop the server.
            _server.Stop()
        Catch __ As OperationCanceledException
            ' When the stopping token is canceled, for example, a call made from services.msc,
            ' we shouldn't exit with a non-zero exit code. In other words, this is expected...
        Catch ex As Exception
            _logger.LogError(ex, "{Message}", ex.Message)

            ' Terminates this process and returns an exit code to the operating system.
            ' This is required to avoid the 'BackgroundServiceExceptionBehavior', which
            ' performs one of two scenarios:
            ' 1. When set to "Ignore": will do nothing at all, errors cause zombie services.
            ' 2. When set to "StopHost": will cleanly stop the host, and log errors.
            ' 
            ' In order for the Windows Service Management system to leverage configured
            ' recovery options, we need to terminate the process with a non-zero exit code.
            Environment.Exit(1)
        End Try
    End Function
End Class
See Also

Installed Examples - Server Console

Installed Examples - Server Windows Forms

Installed Examples - Server Windows Service

Installed Examples - Server Worker Service

Reference

Development

QuickOPC

External